package main

import (
	"database/sql"
	"encoding/json"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"hello/conf"
	"hello/controllers"
	"hello/lib"
	"hello/lib/mqttLib"
	"hello/lib/mysqlLib"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"time"
)

const (
	AlarmSaveDuration   = 10
	HistorySaveDuration = 5
)

func AccessTokenTimer() {
	t1 := time.Tick(7200 * time.Second)
	for {
		select {
		case <- t1:
			lib.SaveAccessToken()
		}
	}
}
type DeviceStatus struct {
	GatewayID string `json:"gateway_id"`
	Status string `json:"status"`
}

var alarmDuration int
var historyDuration int
var que = make(chan string, 10)
func DeviceOnline() {
	mqttconfig:= mqttLib.MqttConfig{mqttLib.MqttServer, "client" + strconv.Itoa(rand.Intn(10000))}
	c := mqttLib.NewMqttClient(mqttconfig, func(client mqtt.Client, msg mqtt.Message) {
		log.Print(msg)
	})
	if t := c.Connect();t.Wait() && t.Error() != nil {
		log.Println(t.Error())
	}
	//defer c.Disconnect(3*1000)
	if token := c.Subscribe(mqttLib.DeviceOnline, 0, onlineHandler); token.Wait() && token.Error() != nil {
		log.Printf("Error : Publish message error : %s", token.Error().Error())
	}
}
func onlineHandler (client mqtt.Client, message mqtt.Message) {
	ds := DeviceStatus{}
	err := json.Unmarshal(message.Payload(), &ds)
	if err != nil {
		log.Printf("unmarshal data error : %s", err.Error())
	}
	que <- ds.GatewayID
}

func onHistoryDataArrived(client mqtt.Client, message mqtt.Message) {
	var msg controllers.CLJBHistoryData
	err := json.Unmarshal(message.Payload(), &msg)
	if err != nil {
		log.Printf("unmarshal message data error : %s", err.Error())
	}
	db := mysqlLib.NewConnection()
	ad := getAlarmData(msg.Data.ErrorInfo)
	if historyDuration == 0 {
		saveHistory(db, msg)
		historyDuration = HistorySaveDuration
	} else {
		historyDuration--
	}
	if msg.Data.ErrorInfo != 0 && alarmDuration == 0 {
		saveAlarm(db, msg)
		wechatAlarm(msg.GatewayID, ad)
		alarmDuration = AlarmSaveDuration
	} else {
		alarmDuration--
	}
}
func GetGatewayID() {
	mqttconfig:= mqttLib.MqttConfig{"tcp://dcharles.work:1883", "client" + strconv.Itoa(rand.Intn(10000))}

	for {
		select {
			case gateway := <- que:
				go func() {
					c := mqttLib.NewMqttClient(mqttconfig, func(client mqtt.Client, msg mqtt.Message) {
						log.Print(msg)
					})
					if t := c.Connect();t.Wait() && t.Error() != nil {
						log.Println(t.Error())
					}
					topic := gateway + "/data"
					if token := c.Subscribe(topic, 0, onHistoryDataArrived); token.Wait() && token.Error() != nil {
						log.Printf("subscribe gateway error, gateway id :%s", gateway)
					}
				}()
		}
	}
}
func saveHistory(conn *sql.DB, historyData controllers.CLJBHistoryData) {
	//todo
	_, err := conn.Exec("insert into hh_history_data (gateway, device_type, temp, speed, status, mode, power_lost_mem) values (?, ?, ?, ?, ?, ?, ?);",
		historyData.GatewayID,
		historyData.Data.MachineType,
		historyData.Data.DisplayTemp,
		historyData.Data.DisplaySpeed,
		historyData.Data.RunningStatus,
		historyData.Data.CurrentMode,
		historyData.Data.PowerLostMemory,
		)
	if err != nil {
		log.Printf("save history data error : %s", err.Error())
	}
}

func saveAlarm(conn *sql.DB, historyData controllers.CLJBHistoryData) {
	//todo
	data := getAlarmData(historyData.Data.ErrorInfo)
	_, err := conn.Exec("insert into hh_device_alarm (gateway_id, device_type, inner_sensor_err, inner_sensor_temp_alarm, " +
		"external_sensor_err, external_sensor_over_temp, mechanical_limit_temp_over_temp, mechanical_limit_sensor_err, " +
		"motor_stalling, external_sensor_drop, end_of_time, external_sensor_not_plugged) values (?,?,?,?,?,?,?,?,?,?,?,?);",
		historyData.GatewayID,
		historyData.Data.MachineType,
		data.InnerSensorErr,
		data.InnerSensorTempAlarm,
		data.ExternalSensorErr,
		data.ExternalSensorOverTemp,
		data.MechanicalLimitTempOverTemp,
		data.MechanicalLimitSensorErr,
		data.MotorStalling,
		data.ExternalSensorDrop,
		data.EndOfTime,
		data.ExternalSensorNotPlugged,
	)
	if err != nil {
		log.Printf("save alarm data error : %s", err.Error())
	}
}
func isPositiveInt(e int) int {
	if e > 0 {
		return 1
	} else {
		return 0
	}
}
func getAlarmData(e int) controllers.CLJBAlarm {
	return controllers.CLJBAlarm{
		InnerSensorErr:              uint(isPositiveInt(e & 32768)),   //0
		InnerSensorTempAlarm:        uint(isPositiveInt(e & 16384)),   //1
		ExternalSensorErr:           uint(isPositiveInt(e & 8192)),    //2
		ExternalSensorOverTemp:      uint(isPositiveInt(e & 4096)),
		MechanicalLimitTempOverTemp: uint(isPositiveInt(e & 2048)),
		MechanicalLimitSensorErr:    uint(isPositiveInt(e & 1024)),
		MotorStalling:               uint(isPositiveInt(e & 512)),     //6
		ExternalSensorDrop:          uint(isPositiveInt(e & 256)),
		EndOfTime:                   uint(isPositiveInt(e & 128)),
		ExternalSensorNotPlugged:    uint(isPositiveInt(e & 64)),      //9
	}
}
//todo
func wechatAlarm(gatewayID string, info controllers.CLJBAlarm) {
	/*
		{{first.DATA}} 设备号：{{deviceID.DATA}} 发生时间：{{time.DATA}} 故障信息：{{alarmInfo.DATA}} {{remark.DATA}}
	*/
	//todo :联合查询

	// from gatewayid get userid
	sqlStr := "select user_id from hh_gateway_band_RT where gateway_id = ?;"
	res, err := mysqlLib.Query(sqlStr, gatewayID)
	if err != nil {
		log.Printf("get userid from database error : %s", err.Error())
	}
	for _, userid := range res {
		// from userid get openid
		sqlStr := "select openid from hh_user where user_id = ?;"
		res, err := mysqlLib.Query(sqlStr, userid)
		if err != nil {
			log.Printf("get openid from database error ： %s", err.Error())
		}
		openid, ok := res[0].(string)
		if !ok {
			log.Print("get openid type error")
		}
		token := lib.GetAccessToken()
		// convert alarm info to string

		am := &controllers.AlarmMessage{}
		am.Touser = openid
		am.TemplateID = conf.AlarmTemplate1
		am.Url = "http://weixin.qq.com/download"
		t := time.Now().Format("2006-01-02 15:04:05")
		alarm := alarm2String(info)
		am.Data = controllers.AlarmContent{
			First: struct {
				Value string `json:"value"`
				Color string `json:"color"`
			}{"设备故障！", "#173177"},
			DeviceID: struct {
				Value string `json:"value"`
				Color string `json:"color"`
			}{gatewayID, "#173177"},
			Time: struct {
				Value string `json:"value"`
				Color string `json:"color"`
			}{t, "#173177"},
			AlarmInfo: struct {
				Value string `json:"value"`
				Color string `json:"color"`
			}{alarm,"#173177"},
			Remark: struct {
				Value string `json:"value"`
				Color string `json:"color"`
			}{"请检查设备！", "#173177"},
		}
		url := strings.Replace(conf.AlarmTemplateUrl, "TOKEN", token, 1)
		alarmMsg, err := json.Marshal(am)
		resp, err := http.Post(url, "application/json", strings.NewReader(string(alarmMsg)))
		if err != nil {
			log.Printf("post alarm msg to Wechat error : %s", err.Error())
		}
		result, err := ioutil.ReadAll(resp.Body)
		log.Printf("alarm response : %s", string(result))
	}
}

func alarm2String(alarm controllers.CLJBAlarm) string {
	alarmStr := ""
	if alarm.InnerSensorErr == 1 {
		alarmStr += "内部传感器故障\n"
	} else if alarm.InnerSensorTempAlarm == 1 {
		alarmStr += "内部传感器温度报警\n"
	} else if alarm.ExternalSensorErr == 1 {
		alarmStr += "外部传感器故障\n"
	} else if alarm.ExternalSensorOverTemp == 1 {
		alarmStr += "外部传感器超温\n"
	} else if alarm.MechanicalLimitTempOverTemp == 1 {
		alarmStr += "机械限温超温\n"
	} else if alarm.MechanicalLimitSensorErr == 1 {
		alarmStr += "机械限温传感器故障\n"
	} else if alarm.MotorStalling == 1 {
		alarmStr += "电机堵转\n"
	} else if alarm.ExternalSensorDrop == 1 {
		alarmStr += "外部传感器跌落\n"
	} else if alarm.EndOfTime == 1 {
		alarmStr += "定时结束\n"
	} else if alarm.ExternalSensorNotPlugged == 1 {
		alarmStr += "外部传感器未插入\n"
	}
	return alarmStr
}